﻿using System;
using System.Linq;
using System.Collections.Generic;
using DocumentFormat.OpenXml.Packaging;
using Expect.Core;
using System.Reflection;

namespace Expect.Report.Word.Processors
{
    class ProcessorManager
    {
        Dictionary<Type, IProcessor> processors = new Dictionary<Type, IProcessor>();

        private IProcessor defaultProcessor = new DefaultProcessor();

        private ReportConfiguration _conf;

        private List<WordStyleRef> _stylesCatalog;

        public ProcessorManager(ReportConfiguration conf)
        {
            this.RegisterSelfContainedProcessors();
            _conf = conf;
        }

        private void RegisterSelfContainedProcessors()
        {
            defaultProcessor.ResolveStyle = this.GetStyle;
            var types = this.GetType().Assembly.GetTypes().Where(t => t.GetInterfaces().Any(tInt => tInt == typeof(IProcessor)));
            foreach (var type in types)
            {
                var interfaces = type.GetInterfaces();
                var baseType =
                    interfaces.FirstOrDefault(
                        t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IProcessor<>));
                if (baseType != null)
                {
                    var generic = baseType.GetGenericArguments()[0];
                    var instance = (IProcessor)Activator.CreateInstance(type);
                    instance.ResolveStyle = GetStyle;

                    processors.Add(generic, instance);
                }
            }
        }

        public void RegisterProcessor<T>(IProcessor<T> processor) where T : IFixtureImplementation
        {
            RegisterProcessor(typeof(T), processor);
        }

        public void RegisterProcessor(Type processedType, IProcessor processor)
        {
            processors.Add(processedType, processor);
        }

        public void ProcessFixture(Assembly target, WordprocessingDocument document, FixtureDefinition definition)
        {
            IProcessor processor = defaultProcessor;

            if (definition.ClassType != null && processors.ContainsKey(definition.ClassType))
            {
                processor = processors[definition.ClassType];
            }
            else if (definition.ClassType != null && definition.ClassType.BaseType != null && processors.ContainsKey(definition.ClassType.BaseType))
            {
                processor = processors[definition.ClassType.BaseType];
            }

            processor.Document = document;
            processor.Fixture = definition;
            processor.Configuration = _conf;
            processor.TargetAssembly = target;

            processor.Apply();
        }

        public void SetStyles(List<WordStyleRef> styles)
        {
            _stylesCatalog = styles;
        }

        List<string> unknownStyles = new List<string>();
        public void LogUnknownStyles()
        {
            foreach (var unknownStyle in unknownStyles)
            {
                Console.WriteLine("Unknown style " + unknownStyle);
            }   
        }

        public string GetStyle(string idref)
        {
            if (_stylesCatalog == null) return idref;

            var style = _stylesCatalog.FirstOrDefault(s => string.Compare(s.Id, idref, true) == 0);
            if (style == null)
                style = _stylesCatalog.FirstOrDefault(s => string.Compare(s.Name, idref, true) == 0);

            if (style == null)
            {
                if (!unknownStyles.Contains(idref))
                    unknownStyles.Add(idref);

                return null;
            }

            return style.Id;
        }        
    }
}
